﻿using System.Net.Sockets;
using System.Net;
using System.Text.Json;
using System.Text.Json.Serialization;
public class ChattingServer
{
    public static Socket socket;
    public static List<ClinetInfo> clients = new List<ClinetInfo>();
    public class ClinetInfo
    {
        public Socket socket;
        public byte[] buffer = new byte[1024];
        public int index;
        public ClinetInfo(Socket socket)
        {
            this.socket = socket;
        }
    }
    public static readonly int WIDTH = 10;
    public static readonly int HEIGHT = 10;
    public static bool[] colorTaken = new bool[2] { false, false };// 0 is for black and 1 is for white.
    public static int[,] map = new int[WIDTH, HEIGHT];
    public static bool checkWinFlag = false;
    public static void Main(string[] argv)
    {
        try
        {
            for (int i = 0; i < WIDTH; i++)
                for (int j = 0; j < HEIGHT; j++)
                    map[i, j] = -1;
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Bind(new IPEndPoint(IPAddress.Any, 10001));
            socket.Listen(2);
            socket.BeginAccept(AcceptCallback, socket);
            Console.ReadLine();

        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }
    public static void AcceptCallback(IAsyncResult result)
    {
        try
        {
            Socket socket = (Socket)result.AsyncState;
            Socket client = socket.EndAccept(result);
            ClinetInfo info = new ClinetInfo(client);
            clients.Add(info);
            client.BeginReceive(info.buffer, 0, 1024, 0, ReceiveCallback, info);
            socket.BeginAccept(AcceptCallback, socket);
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }
    public static async void ReceiveCallback(IAsyncResult result)
    {
        try
        {
            ClinetInfo info = (ClinetInfo)result.AsyncState;
            int count = info.socket.EndReceive(result);
            if (count == 0)
            {
                colorTaken[info.index] = false;
                info.socket.Close();
                clients.Remove(info);
                Console.WriteLine("one client left");
                return;
            }
            string jsonString = System.Text.Encoding.UTF8.GetString(info.buffer, 0, count);
            Console.WriteLine(jsonString);
            ProtoBase proto = Decode(jsonString);

            if (proto is MessageProto)
                ReceiveMessage(proto as MessageProto);
            else if (proto is PlayProto)
                ReceivePlay(proto as PlayProto);
            else if (proto is ReadyProto)
                ReceiveReady(info, proto as ReadyProto);
            info.socket.BeginReceive(info.buffer, 0, 1024, 0, ReceiveCallback, info);
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }
    private static ProtoBase Decode(string str)
    {
        var args = str.Split('|');
        ProtoBase proto = null;
        switch (args[0])
        {
            case "ready":
                proto = JsonSerializer.Deserialize<ReadyProto>(args[1]);
                break;
            case "play":
                proto = JsonSerializer.Deserialize<PlayProto>(args[1]);
                break;
            case "message":
                proto = JsonSerializer.Deserialize<MessageProto>(args[1]);
                break;
            default:
                Console.WriteLine("Decode Wrong Arguments");
                break;
        }
        return proto;
    }
    private static byte[] Encode(ProtoBase proto)
    {
        string res = proto.name;
        //res = res + "|" + JsonSerializer.Serialize(proto);
        switch (res)
        {
            case "color":
                res = res + "|" + JsonSerializer.Serialize<ColorProto>(proto as ColorProto);
                break;
            case "play":
                res = res + "|" + JsonSerializer.Serialize<PlayProto>(proto as PlayProto);
                break;
            case "message":
                res = res + "|" + JsonSerializer.Serialize<MessageProto>(proto as MessageProto);
                break;
            case "ready":
                res = res + "|" + JsonSerializer.Serialize<ReadyProto>(proto as ReadyProto);
                break;
            case "win":
                res = res + "|" + JsonSerializer.Serialize<WinProto>(proto as WinProto);
                break;
            default:
                Console.WriteLine("Encode Wrong Arguments");
                break;
        }
        Console.WriteLine("Encode: " + res);
        return System.Text.Encoding.UTF8.GetBytes(res);
    }

    private static void ReceivePlay(PlayProto playProto)
    {
        foreach (var c in clients)
            c.socket.Send(Encode(playProto));
        map[playProto.x, playProto.y] = playProto.color;
        CheckWin(playProto);
        if (checkWinFlag)
        {
            foreach (var c in clients)
                c.socket.Send(Encode(new WinProto() { name = "win", color = playProto.color }));
        }
    }
    private static async void ReceiveReady(ClinetInfo info, ReadyProto readyProto)
    {
        Random r = new Random();
        int v = r.Next(0, 2);
        if (!colorTaken[v])
        {
            colorTaken[v] = true;
            info.socket.Send(Encode(new ColorProto() { name = "color", color = v }));

            info.socket.Send(Encode(new MessageProto() { name = "message", message = "系统: 你已经就绪，请等待另一位玩家。" }));

            info.index = v;
        }
        else if (!colorTaken[1 - v])
        {
            colorTaken[1 - v] = true;
            info.socket.Send(Encode(new ColorProto() { name = "color", color = 1 - v }));

            info.socket.Send(Encode(new MessageProto() { name = "message", message = "系统: 你已经就绪，请等待另一位玩家。" }));

            info.index = 1 - v;
        }
        else
        {
            Console.WriteLine("Ready Wrong Arguments");
        }
        await Task.Delay(200);
        if (colorTaken[0] && colorTaken[1])
        {
            foreach (var c in clients)
            {
                c.socket.Send(Encode(readyProto));
                await Task.Delay(200);
                c.socket.Send(Encode(new MessageProto() { name = "message", message = "系统: 所有玩家已就位，对局开始。" }));
            }
        }
    }
    private static void ReceiveMessage(MessageProto messageProto)
    {
        foreach (var c in clients)
            c.socket.Send(Encode(messageProto));
    }
    private static void CheckWin(PlayProto proto)
    {
        checkWinFlag = false;
        DFS(0, proto.color, proto.x, proto.y, proto.x, proto.y, 1);
        DFS(1, proto.color, proto.x, proto.y, proto.x, proto.y, 1);
        DFS(2, proto.color, proto.x, proto.y, proto.x, proto.y, 1);
        DFS(3, proto.color, proto.x, proto.y, proto.x, proto.y, 1);
    }
    private static void DFS(int mode, int c, int x1, int y1, int x2, int y2, int count)
    {
        int oldCount = count;
        if (count >= 5)
        {
            checkWinFlag = true;
            return;
        }
        if (mode == 0)
        {
            if (x1 - 1 >= 0 && y1 + 1 < HEIGHT)
            {
                if (map[x1 - 1, y1 + 1] == c)
                {
                    x1--;
                    y1++;
                    count++;
                }
            }
            if (x2 + 1 < WIDTH && y2 - 1 >= 0)
            {
                if (map[x2 + 1, y2 - 1] == c)
                {
                    x2++;
                    y2--;
                    count++;
                }
            }
        }
        if (mode == 1)
        {
            if (x1 - 1 >= 0)
            {
                if (map[x1 - 1, y1] == c)
                {
                    x1--;
                    count++;
                }
            }
            if (x2 + 1 < WIDTH)
            {
                if (map[x2 + 1, y2] == c)
                {
                    x2++;
                    count++;
                }
            }
        }
        if (mode == 2)
        {
            if (x1 - 1 >= 0 && y1 - 1 >= 0)
            {
                if (map[x1 - 1, y1 - 1] == c)
                {
                    x1--;
                    y1--;
                    count++;
                }
            }
            if (x2 + 1 < WIDTH && y2 + 1 < HEIGHT)
            {
                if (map[x2 + 1, y2 + 1] == c)
                {
                    x2++;
                    y2++;
                    count++;
                }
            }
        }
        if (mode == 3)
        {
            if (y1 - 1 >= 0)
            {
                if (map[x1, y1 - 1] == c)
                {
                    y1--;
                    count++;
                }
            }
            if (y2 + 1 < HEIGHT)
            {
                if (map[x2, y2 + 1] == c)
                {
                    y2++;
                    count++;
                }
            }
        }
        if (count > oldCount)
            DFS(mode, c, x1, y1, x2, y2, count);
    }
}